<template>
  <div class="tool-content-page w-1200">
    <div class="tool-classify gloab-scroll-bar">
      <div
        class="tool-classify-item"
        v-for="item in toolList"
        :class="{ selected: item.active }"
        :key="item.id"
        @click="scrollToPosition(item)">
        <i :class="item.icon"></i>
        <p>{{ item.name }}</p>
      </div>
    </div>
    <div class="tool-modal">
      <div
        class="tool-modal-item"
        v-for="item in toolList"
        :key="item.id">
        <div class="title" :id="`classify-${item.id}`">
          <p>{{ item.name }} </p>
        </div>
        <div class="tool-list" v-if="item.children?.length">
          <div
            class="tool-list-item"
            v-for="itemName in item.children"
            :key="itemName.id"
            @click="openWindowHref(itemName.url)"
            :id="`tool-${itemName.id}`">
            <div class="basic-info">
              <img :src="itemName.logo || require('~/assets/images/folder.png')" alt="">
              <p class="name">{{ itemName.name }}</p>
            </div>
            <div class="desc">
              <p>{{ itemName.description }}</p>
            </div>
            <div class="tool-time">
              <p
                v-if="!itemName.collected"
                class="collect"
                @click.stop="handleCollectTool($event, itemName)">
                收藏<i class="ri-user-star-line"></i>
              </p>
              <p
                v-if="itemName.collected"
                @click.stop="handleCollectTool($event, itemName)"
                class="has-collect">
                已收藏<i class="ri-heart-2-fill"></i>
              </p>
              <p>{{ itemName.createTime }}</p>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="right-user-modal">
      <div class="search-tool-modal">
        <input v-model="searchKeywords" type="text" placeholder="输入工具名称进行检索" />
        <i class="ri-search-2-line"></i>
        <div class="fuzzy-tools" v-if="searchKeywords">
          <div class="fuzzy-tools-item" v-for="item in fuzzyToolList" :key="item.id" @mousedown="handleAnchorPointTool(item.id)">
            <img :src="item.logo || require('~/assets/images/folder.png')" alt="" class="logo">
            <p class="name">
              <span>{{ item.name }}</span>
              <em class="desc">{{ item.description }}</em>
            </p>
          </div>
          <a-empty
            v-show="!fuzzyToolList.length"
            description="没有找到复合条件的结果"
            :image="require('~/assets/images/nodata.png')"
            :imageStyle="{ height: '60px', margin: '40px auto 0', fontSize: '10px' }"
          />
        </div>
      </div>
      <div class="profile">
        <HeadAvatar
          :width="42"
          :height="42"
          :img="!userInfo ? require('~/assets/images/default.svg') : userInfo.avatar || 'https://image.zdxblog.cn/picture/16849805191921684980519192.png'"
        />
        <div class="user-info">
          <p class="nickname">
            <span v-if="userInfo">{{ userInfo.nickname }}</span>
            <em v-else @click="$store.commit('UPDATE_LOGIN_VISIBLE', true)">登录/注册</em>
          </p>
          <p class="job">{{ userInfo ? userInfo.job || '' : '收藏工具后可以快速找到它' }}</p>
        </div>
      </div>
      <div class="self-collect">
        <div class="title"><img src="~/assets/images/collect-icon.svg" alt=""></div>
        <div class="collect-list">
          <a class="collect-list-item" :href="item.url" target="_blank" v-for="item in collectList.children.slice(0, 5)" :key="item.id">
            <img :src="item.logo || require('~/assets/images/folder.png')" alt="" class="icon">
            <div class="info">
              <p class="name">{{ item.name }}</p>
              <p class="desc">{{ item.description }}</p>
            </div>
          </a>
        </div>
        <a-empty
          v-show="!collectList.children.length"
          :description="!userInfo ? '登录后可查看收藏的工具' : '您还未收藏工具'"
          :image="require('~/assets/images/nodata.png')"
          :imageStyle="{ height: '60px', margin: '40px auto 20px', fontSize: '12px' }"
        />
        <nuxt-link
          target="_blank"
          v-show="collectList.children.length > 5"
          class="vi-more"
          :to="`/user/${userInfo.id}/collect?type=tool`">我的所有收藏<i class="ri-arrow-right-s-line"></i></nuxt-link>
      </div>
      <SiteCopyRight />
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
import { getElementDistanceFromTop, debounce } from '~/util'
import SiteCopyRight from '~/components/SiteCopyRight'

export default {
  name: 'ToolPage',
  layout: 'BaseLayout',
  components: { SiteCopyRight },
  async asyncData ({ $axios }) {
    const { data } = await $axios.get('/gimishare/api/v1/sites/list-tree')
    const toolList = data.slice(1)
    const collectList = data.slice(0, 1)[0]
    return {
      toolList,
      collectList
    }
  },
  head () {
    return {
      title: 'GimiShare 工具列表'
    }
  },
  data () {
    return {
      collectIng: false,
      toolList: [],
      clickState: false,
      visibleElementId: [],
      searchKeywords: '',
      debounceFindTools: null,
      fuzzyToolList: []
    }
  },

  mounted () {
    this.bindIntersectionObserver()
    this.debounceFindTools = debounce(this.findToolListByName, 300)
  },

  methods: {
    handleAnchorPointTool (toolId) {
      const dom = document.querySelector(`.tool-list #tool-${toolId}`)
      const { top, height } = dom.getBoundingClientRect()
      if (top < 80 || top > window.screen.height - height * 2) {
        const c = 350 - top
        this.scrollDocument(
          Math.min(
            Math.max(0, document.documentElement.scrollTop - c),
            document.querySelector('#__layout').clientHeight - window.screen.height
          ),
          top < 80 ? 'up' : 'down',
          dom
        )
        return
      }
      this.blinkHighLightDom(dom)
    },

    async handleCollectTool (event, toolItem) {
      if (!this.userInfo) {
        return this.$store.commit('UPDATE_LOGIN_VISIBLE', true)
      }
      if (this.collectIng) return
      this.collectIng = true
      const toolId = toolItem.id
      const targetNode = event.currentTarget.parentNode.parentNode
      try {
        const API = toolItem.collected ? '/gimishare/api/v1/users/uncollecttool' : '/gimishare/api/v1/users/collecttool'
        await this.$axios.get(`${API}?toolId=${toolId}`)
        toolItem.collected = Number(!toolItem.collected)
        if (toolItem.collected) {
          const clientRect = targetNode.getBoundingClientRect()
          const cloneNode = targetNode.cloneNode(true)
          cloneNode.classList.add('clone-item')
          cloneNode.style.cssText = `width: ${clientRect.width}px; top: ${clientRect.top}px; left: ${clientRect.left}px`
          targetNode.parentNode.appendChild(cloneNode)
          this.elementNodeMoveToPosition(cloneNode, document.querySelector('.head-avatar'), 200)
          this.collectList.children.push(JSON.parse(JSON.stringify(toolItem)))
        } else {
          const idx = this.collectList.children.findIndex(o => o.id === toolItem.id)
          if (idx > -1) {
            this.collectList.children.splice(idx, 1)
          }
        }
      } catch (e) {} finally {
        this.collectIng = false
      }
    },

    elementNodeMoveToPosition (sourceElement, targetElement, duration) {
      let translateX = 0
      let translateY = 0
      let globalAlpha = 1
      const sourcePosition = sourceElement.getBoundingClientRect()
      const targetPosition = targetElement.getBoundingClientRect()
      const animationTime = Math.ceil(duration / 16)
      const alphaSpeed = 1 / animationTime
      const intervalX = (sourcePosition.left - targetPosition.left + (sourcePosition.width - targetPosition.width) / 2) / animationTime
      const intervalY = (sourcePosition.top - targetPosition.top + (sourcePosition.height - targetPosition.height) / 2) / animationTime
      const elementMoveTimer = setInterval(() => {
        if (globalAlpha <= 0) {
          clearInterval(elementMoveTimer)
          sourceElement.remove()
          return
        }
        translateX -= intervalX
        translateY -= intervalY
        sourceElement.style.transform = `translate(${translateX}px, ${translateY}px) scale(${globalAlpha})`
        sourceElement.style.opacity = globalAlpha
        globalAlpha -= alphaSpeed
      }, 16)
    },

    scrollToPosition (navigatorItem) {
      this.clickState = true
      clearTimeout(this.scrollTimer)
      for (let i = 0; i < this.toolList.length; i++) {
        this.$set(this.toolList[i], 'active', navigatorItem.id === this.toolList[i].id)
      }
      const ele = document.querySelector(`#classify-${navigatorItem.id}`)
      const scrollTop = document.documentElement.scrollTop
      const offsetTop = getElementDistanceFromTop(ele)
      const maxScrollTop = document.querySelector('#__layout').clientHeight - document.documentElement.clientHeight
      const scrollPostion = Math.min(offsetTop - 73, maxScrollTop)
      this.scrollDocument(scrollPostion, scrollPostion < scrollTop ? 'up' : 'down')
    },

    /**
     * 滚动到指定位置
     * @param { position } string
     * @param { direction } string
     */
    scrollDocument (position, direction, dom) {
      const scrollTop = document.documentElement.scrollTop
      if (direction === 'up') {
        const nextScrollTop = Math.max(position, scrollTop - (scrollTop - position) * 0.18)
        document.documentElement.scrollTop = nextScrollTop
        if (nextScrollTop > position) {
          this.scrollTimer = setTimeout(() => {
            this.scrollDocument(position, direction, dom)
          }, 16)
        } else {
          this.clickState = false
          clearTimeout(this.scrollTimer)
          dom && this.blinkHighLightDom(dom)
        }
      } else {
        const nextScrollTop = Math.min(position, Math.ceil(scrollTop + (position - scrollTop) * 0.18))
        document.documentElement.scrollTop = nextScrollTop
        if (nextScrollTop < position) {
          this.scrollTimer = setTimeout(() => {
            this.scrollDocument(position, direction, dom)
          }, 16)
        } else {
          this.clickState = false
          clearTimeout(this.scrollTimer)
          dom && this.blinkHighLightDom(dom)
        }
      }
    },

    bindIntersectionObserver () {
      const c = debounce(this.setTocsCurrentActive, 10)
      const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
          if (entry.intersectionRatio === 1) {
            this.visibleElementId[entry.boundingClientRect.top < 200 ? 'unshift' : 'push'](entry.target.id.split('-')[1])
          } else {
            const idx = this.visibleElementId.findIndex(id => id === entry.target.id.split('-')[1])
            if (idx > -1) {
              this.visibleElementId.splice(idx, 1)
            }
          }
          if (!this.clickState) {
            c(this.visibleElementId)
          }
        })
      }, { threshold: 1, rootMargin: '100px 0px 200px 0px' })
      for (let i = 0; i < this.toolList.length; i++) {
        observer.observe(document.querySelector(`#classify-${this.toolList[i].id}`))
      }
    },

    blinkHighLightDom (dom) {
      dom.classList.add('blink-dom')
      setTimeout(() => {
        dom.classList.remove('blink-dom')
      }, 2400)
    },

    async findToolListByName () {
      if (this.searchKeywords) {
        const data = await this.$axios.get(`/gimishare/api/v1/sites/list-by-name?name=${this.searchKeywords}`)
        this.fuzzyToolList = data.data.slice(0, 6)
      } else {
        this.fuzzyToolList = []
      }
    },

    setTocsCurrentActive (ids) {
      if (!ids?.length) return
      const id = ids[0]
      for (let i = 0; i < this.toolList.length; i++) {
        this.$set(this.toolList[i], 'active', Number(id) === this.toolList[i].id)
      }
    },

    openWindowHref (url) {
      window.open(url, 'target')
    }
  },

  watch: {
    searchKeywords (name) {
      this.debounceFindTools(name)
    }
  },

  computed: {
    ...mapState({
      userInfo: (state) => state.userInfo
    })
  }
}
</script>

<style lang="less" scoped>
.tool-content-page {
  padding-top: 12px;
  display: flex;
  align-items: flex-start;
  .tool-classify {
    width: 180px;
    background: #fff;
    padding: 12px;
    border-radius: 4px;
    position: fixed;
    top: 73px;
    bottom: 12px;
    &-item {
      display: flex;
      align-items: center;
      height: 38px;
      padding: 0 20px;
      cursor: pointer;
      border-radius: 4px;
      margin: 4px 0;
      p {
        font-size: 13px;
      }
      &.selected {
        background: #80dd9a30;
        color: var(--primary-color);
      }
      i {
        position: relative;
        top: 1px;
        font-size: 18px;
        margin-right: 10px;
      }
    }
  }
  .tool-modal {
    flex: 1;
    width: 0;
    margin: 0 12px;
    padding-left: 180px;
    &-item {
      .title {
        margin-bottom: 12px;
        background: #fff;
        padding: 12px 32px ;
        border-radius: 4px;
        position: relative;
        &.no-shadow {
          box-shadow: none;
          border-radius: 4px 4px 0 0;
        }
        &:after {
          content: "";
          width: 4px;
          height: 12px;
          position: absolute;
          left: 18px;
          top: 17px;
          background: -webkit-linear-gradient(315deg,#42d392 25%,#647eff);
        }
        p {
          font-weight: 600;
          font-size: 14px;
          background: -webkit-linear-gradient(315deg,#42d392 25%,#647eff);
          background-clip: text;
          -webkit-background-clip: text;
          -webkit-text-fill-color: transparent;
          display: inline-block;
        }
      }
    }
    .tool-list {
      display: flex;
      flex-wrap: wrap;
      position: relative;
      &-item {
        background: #fff;
        width: calc((100% - 24px) / 3);
        padding: 8px 12px;
        margin-bottom: 12px;
        display: flex;
        flex-direction: column;
        justify-content: space-between;
        cursor: pointer;
        border-radius: 4px;
        &:not(:nth-child(3n)) {
          margin-right: 12px;
        }
        .basic-info {
          display: flex;
          align-items: center;
          img {
            display: block;
            width: 28px;
            height: 28px;
            border-radius: 2px;
            object-fit: cover;
            margin-right: 10px;
          }
          .name {
            font-size: 14px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
            color: #000;
            flex: 1;
            width: 0;
          }
        }
        .desc {
          margin: 4px 0;
          p {
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            font-size: 12px;
            line-height: 22px;
            color: #999;
          }
        }
        .tool-time {
          font-size: 12px;
          color: #ccc;
          display: flex;
          align-items: center;
          justify-content: space-between;
          height: 22px;
          p {
            &.collect {
              display: flex;
              align-items: center;
              visibility: hidden;
              pointer-events: none;
              i {
                color: #ccc;
                font-size: 14px;
                margin-left: 4px;
                position: relative;
                top: 1px;
              }
            }
            &.has-collect {
              i {
                margin-left: 6px;
              }
              color: var(--primary-color);
            }
          }
        }
        &:hover {
          p {
            &.collect {
              visibility: visible;
              pointer-events: all;
            }
          }
        }
        &.clone-item {
          position: fixed;
          pointer-events: none;
          z-index: 9;
        }
        &.blink-dom {
          animation: blink 2.6s linear 1 forwards;
          background-color: var(--hover-primary-color);
          .name, .desc, .tool-time {
            color: #fff !important;
            p {
              color: #fff !important;
            }
          }
        }
        @keyframes blink {
          0% {
            opacity: 1;
          }
          20% {
            opacity: 0.3;
          }
          40% {
            opacity: 0.8;
          }
          60% {
            opacity: 0.3;
          }
          100% {
            opacity: 1;
          }
        }
      }
    }
  }

  .right-user-modal {
    width: 250px;
    position: sticky;
    top: 73px;
    .search-tool-modal {
      display: flex;
      margin-bottom: 12px;
      padding: 8px 12px;
      background-color: #fff;
      border-radius: 4px;
      align-items: center;
      position: relative;
      z-index: 99;
      input {
        flex: 1;
        width: 0;
        border: none;
        outline: none;
        &:focus ~ .fuzzy-tools {
          display: block;
        }
      }
      .fuzzy-tools {
        position: absolute;
        top: 42px;
        left: 0;
        right: 0;
        background-color: #fff;
        box-shadow: 0 4px 15px 4px rgba(28, 40, 39, 0.05098);
        border-radius: 4px;
        padding: 12px 0;
        display: none;
        &-item {
          display: flex;
          padding: 6px 12px;
          align-items: center;
          cursor: pointer;
          .logo {
            width: 24px;
            height: 24px;
            object-fit: cover;
            margin-right: 8px;
          }
          .name {
            font-size: 12px;
            color: #000;
            flex: 1;
            width: 0;
            display: flex;
            flex-direction: column;
            .desc {
              color: #999;
              font-size: 10px;
              font-style: normal;
              overflow: hidden;
              white-space: nowrap;
              text-overflow: ellipsis;
            }
          }
          &:hover {
            background-color: var(--select-primary-color);
          }
        }
        ::v-deep .ant-empty-description {
          font-size: 12px;
          margin-bottom: 40px;
        }
      }
    }
    .profile {
      display: flex;
      align-items: center;
      background-color: #fff;
      border-radius: 4px;
      padding: 20px;
      .user-info {
        width: 0;
        flex: 1;
        padding-left: 10px;
        .nickname {
          em {
            color: var(--primary-color);
            cursor: pointer;
            font-style: normal;
            font-size: 13px;
          }
          span {
            color: #000;
          }
        }
        .job {
          color: #999;
          font-size: 12px;
        }
      }
    }
    .self-collect {
      background-color: #fff;
      border-radius: 4px;
      padding: 12px;
      margin: 12px 0;
      .title {
        padding: 0 3px;
        img {
          height: 12px;
        }
      }
      .collect-list {
        margin-top: 10px;
        &-item {
          display: flex;
          align-items: center;
          padding: 6px 0;
          .icon {
            display: block;
            width: 22px;
            height: 22px;
            border-radius: 2px;
            object-fit: cover;
            margin-right: 8px;
          }
          .info {
            flex: 1;
            width: 0;
            .name {
              font-size: 12px;
              color: #000;
            }
            .desc {
              color: #999;
              font-size: 11px;
              white-space: nowrap;
              text-overflow: ellipsis;
              overflow: hidden;
            }
          }
        }
      }
      .vi-more {
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 12px;
        color: var(--primary-color);
        margin-top: 8px;
        i {
          font-size: 16px;
        }
      }
      ::v-deep .ant-empty {
        &-description {
          font-size: 12px;
          color: #999;
          margin-bottom: 20px;
        }
      }
    }
  }
}
</style>
